// 条件变量的使用练习
// 条件变量是实现同步机制的 -- 让多线程有顺序的访问
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <vector>
#include <cstdio>

// 全局计数器
int counter = 0;       // 全局计数器
#define TARGET_COUNT 5 // 目标计数
// 全局锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//////////////////// 1. 全局的条件变量 ////////////////////
// pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

//////////////////// 2. 局部的条件变量 ////////////////////

pthread_cond_t cond;

// 工作线程：增加计数器
void *worker(void *args)
{
    std::string name = static_cast<char *>(args);
    for (int i = 0; i < TARGET_COUNT; i++)
    {
        pthread_mutex_lock(&mutex);

        // 增加计数器
        counter++;
        std::cout << "工作线程 : " << name << " - > 计数器增加到 : " << counter << std::endl;

        // 当计数器达到目标值时，通知等待的线程
        if (counter == TARGET_COUNT)
        {
            // 唤醒该条件变量下的一个线程
            pthread_cond_signal(&cond);
            std::cout << " 工作线程: 已发出通知，计数器达到目标值 !" << std::endl;
        }

        pthread_mutex_unlock(&mutex);
        // 短暂休眠，模拟工作耗时
        usleep(100000);
    }
    return nullptr;
}

// 主线程
int main()
{
    //////////////////// 2. 局部的条件变量 ////////////////////

    pthread_cond_init(&cond, nullptr);
    std::vector<pthread_t> threads;
    // 创建5个线程
    for (int i = 1; i <= 5; ++i)
    {
        pthread_t tid;
        char *name = new char[64]; // 开辟一个空间
        snprintf(name, 64, "thread-%d", i);
        int n = pthread_create(&tid, nullptr, worker, name);
        if (n == 0)
        {
            threads.emplace_back(tid);
        }
    }

    // 等待 5 个线程
    for (auto &tid : threads)
    {
        int n = pthread_join(tid, nullptr);
        if (n == 0)
        {
            std::cout << " Join Success ! " << std::endl;
        }
    }

    // 主线程等待计数器达到目标值
    pthread_mutex_lock(&mutex);
    while (counter < TARGET_COUNT)
    {
        printf("主线程: 等待计数器达到 %d...\n", TARGET_COUNT);
        pthread_cond_wait(&cond, &mutex);
        printf("主线程: 被唤醒，当前计数器值为 %d\n", counter);
    }
    pthread_mutex_unlock(&mutex);


    pthread_cond_destroy(&cond);

    return 0;
}